#include <memory.h>
#include "VBoxMPMem.h"
#include "trace.h"

void* RTMemAlloc(size_t cb)
{
    PRTMEMHDR pHdr = (PRTMEMHDR)ExAllocatePool2(POOL_FLAG_NON_PAGED, cb + sizeof(RTMEMHDR), SVGA_MEMTAG);
    if (pHdr == nullptr) {
        LOGE("ExAllocatePool2 memory failed: cb=%u\n", cb);
        return nullptr;
    }
    pHdr->u32Magic = RTMEMHDR_MAGIC;
    pHdr->fFlags = 0; //  fFlags;
    pHdr->cb = (UINT32)cb;
    pHdr->cbReq = (UINT32)cb;

    return pHdr + 1;
}

void RTMemFree(void* pv)
{
    if (pv == nullptr) {
        return;
    }
    PRTMEMHDR pHdr = (PRTMEMHDR)pv - 1; //sizeof(RTMEMHDR);
    if (pHdr->u32Magic == RTMEMHDR_MAGIC) {
        ExFreePoolWithTag(pHdr, SVGA_MEMTAG);
    } else {
        LOGE("invalid PRTMEMHDR magic\n");
    }
}

void* RTMemRealloc(void* pvOld, size_t cbNew)
{
    PRTMEMHDR pHdrOld;

    /* Free. */
    if (!cbNew && pvOld)
    {
        RTMemFree(pvOld);
        return NULL;
    }

    /* Alloc. */
    if (!pvOld)
        return RTMemAlloc(cbNew);

    /*
    * Realloc.
    */
    pHdrOld = (PRTMEMHDR)pvOld - 1;

    if (pHdrOld->u32Magic == RTMEMHDR_MAGIC)
    {
        PRTMEMHDR pHdrNew;

        /* If there is sufficient space in the old block and we don't cause
        substantial internal fragmentation, reuse the old block. */
        if (pHdrOld->cb >= cbNew + RTR0MEM_FENCE_EXTRA
            && pHdrOld->cb - (cbNew + RTR0MEM_FENCE_EXTRA) <= 128)
        {
            pHdrOld->cbReq = (UINT32)cbNew;
            return pvOld;
        }

        pHdrNew = (PRTMEMHDR)RTMemAlloc(cbNew + RTR0MEM_FENCE_EXTRA);

        if (pHdrNew)
        {
            size_t cbCopy = RT_MIN(pHdrOld->cb, pHdrNew->cb);
            memcpy(pHdrNew + 1, pvOld, cbCopy);
            RTMemFree(pHdrOld);
            return pHdrNew + 1;
        } else {
            LOGE("memory alloc failed size=%u\n", cbNew);
        }
    } else {
        LOGE("invalid old hdr magic=0x%x\n", pHdrOld->u32Magic);
    }

    return NULL;
 }